package com.flyemu.share.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSONObject;
import com.blazebit.persistence.PagedList;
import com.flyemu.share.common.Constants;
import com.flyemu.share.controller.Page;
import com.flyemu.share.controller.PageResults;
import com.flyemu.share.dto.AccountDto;
import com.flyemu.share.dto.SalesOrderDto;
import com.flyemu.share.entity.*;
import com.flyemu.share.enums.OrderStatus;
import com.flyemu.share.form.SalesOrderForm;
import com.flyemu.share.repository.SalesOrderDetailRepository;
import com.flyemu.share.repository.SalesOrderRepository;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.OrderSpecifier;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @功能描述: 销售出库单
 * @创建时间: 2024年06月13日
 * @公司官网: www.fenxi365.com
 * @公司信息: 纷析云（杭州）科技有限公司
 * @公司介绍: 专注于财务相关软件开发, 企业会计自动化解决方案
 */
@Service
@Slf4j
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class SalesOrderService extends AbsService {
    private final static QSalesOrder qSalesOrder = QSalesOrder.salesOrder;
    private final static QAdmin qAdmin = QAdmin.admin;
    private final static QSalesOrderDetail qSalesOrderDetail = QSalesOrderDetail.salesOrderDetail;
    private final static QCustomers qCustomers = QCustomers.customers;
    private final static QProducts qProducts = QProducts.products;
    private final SalesOrderRepository salesOrderRepository;
    private final SalesOrderDetailRepository detailRepository;
    private final CodeSeedService codeSeedService;


    public PageResults<SalesOrderDto> query(Page page, Query query) {
        PagedList<Tuple> pagedList = bqf.selectFrom(qSalesOrder).select(qSalesOrder, qCustomers.name)
                .leftJoin(qCustomers).on(qCustomers.id.eq(qSalesOrder.customersId))
                .where(query.builder)
                .orderBy(query.sortSpecifier(), qSalesOrder.id.desc())
                .fetchPage(page.getOffset(), page.getOffsetEnd());
        ArrayList<SalesOrderDto> collect = pagedList.stream().collect(ArrayList::new, (list, tuple) -> {
            SalesOrderDto dto = BeanUtil.toBean(tuple.get(qSalesOrder), SalesOrderDto.class);
            dto.setCustomersName(tuple.get(qCustomers.name));
            list.add(dto);
        }, List::addAll);
        return new PageResults<>(collect, page, pagedList.getTotalSize());
    }

    public List<Dict> toSelect(Long customersId, Long merchantId, Long organizationId) {
        List<Dict> dicts = new ArrayList<>();
        bqf.selectFrom(qSalesOrder)
                .select(qSalesOrder.id, qSalesOrder.code)
                .where(qSalesOrder.customersId.eq(customersId).and(qSalesOrder.orderStatus.eq(OrderStatus.已审核)).and(qSalesOrder.merchantId.eq(merchantId)).and(qSalesOrder.organizationId.eq(organizationId)))
                .fetch().forEach(tuple -> {
                    Dict dict = new Dict().set("id", tuple.get(qSalesOrder.id))
                            .set("code", tuple.get(qSalesOrder.code));
                    dicts.add(dict);
                });
        return dicts;
    }


    public List<SalesOrderDetail> selectLoad(Long orderId, Long merchantId, Long organizationId) {
        return bqf.selectFrom(qSalesOrderDetail)
                .where(qSalesOrderDetail.salesOrderId.eq(orderId).and(qSalesOrderDetail.merchantId.eq(merchantId)).and(qSalesOrderDetail.organizationId.eq(organizationId)))
                .fetch();
    }

    public List<JSONObject> downloadList(Long merchantId, Long organizationId, Query query) {
        List<JSONObject> list = new ArrayList<>();
        bqf.selectFrom(qSalesOrder)
                .select(qSalesOrder, qCustomers.name, qAdmin.name)
                .leftJoin(qCustomers).on(qCustomers.id.eq(qSalesOrder.customersId).and(qCustomers.merchantId.eq(merchantId)).and(qCustomers.organizationId.eq(organizationId)))
                .leftJoin(qAdmin).on(qAdmin.id.eq(qSalesOrder.userId).and(qAdmin.merchantId.eq(merchantId)))
                .orderBy(qSalesOrder.id.desc())
                .where(query.builder).fetch().forEach(tuple -> {
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("销售订单号", tuple.get(qSalesOrder).getCode());
                    jsonObject.put("单据日期", tuple.get(qSalesOrder).getBillDate().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
                    jsonObject.put("下单时间", tuple.get(qSalesOrder).getCreateDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
                    jsonObject.put("客户名称", tuple.get(qCustomers.name));
                    jsonObject.put("订单金额", tuple.get(qSalesOrder).getDiscountedAmount());
                    jsonObject.put("原始金额", tuple.get(qSalesOrder).getTotalAmount());
                    jsonObject.put("优惠金额", tuple.get(qSalesOrder).getDiscountAmount());
                    jsonObject.put("订单状态", tuple.get(qSalesOrder).getOrderStatus().toString());
                    jsonObject.put("备注", tuple.get(qSalesOrder).getRemark());
                    jsonObject.put("制单人", tuple.get(qAdmin.name));
                    list.add(jsonObject);
                });
        return list;
    }

    public BigDecimal queryTotal(Query query) {
        return bqf.selectFrom(qSalesOrder)
                .select(qSalesOrder.discountedAmount.sum())
                .leftJoin(qCustomers).on(qCustomers.id.eq(qSalesOrder.customersId))
                .where(query.builder).fetchFirst();
    }

    public List<Dict> listBy(Long customersId, Long merchantId, Long organizationId) {
        List<Dict> list = new ArrayList<>();
        bqf.selectFrom(qSalesOrder).select(qSalesOrder.code, qSalesOrder.id)
                .where(qSalesOrder.merchantId.eq(merchantId).and(qSalesOrder.customersId.eq(customersId)).and(qSalesOrder.organizationId.eq(organizationId)))
                .orderBy(qSalesOrder.id.desc()).fetch().forEach(tuple -> {
                    Dict dict = new Dict()
                            .set("id", tuple.get(qSalesOrder.id))
                            .set("name", tuple.get(qSalesOrder.code));
                    list.add(dict);
                });
        return list;
    }

    @Transactional
    public void delete(Long orderId, Long merchantId, Long organizationId) {
        jqf.delete(qSalesOrder).where(qSalesOrder.id.eq(orderId).and(qSalesOrder.merchantId.eq(merchantId)).and(qSalesOrder.organizationId.eq(organizationId))).execute();
        jqf.delete(qSalesOrderDetail).where(qSalesOrderDetail.salesOrderId.eq(orderId).and(qSalesOrderDetail.merchantId.eq(merchantId)).and(qSalesOrderDetail.organizationId.eq(organizationId))).execute();
    }

    @Transactional
    public void save(SalesOrderForm orderForm, Long adminId, Long merchantId, Long organizationId, String merchantCode) {
        SalesOrder order = orderForm.getSalesOrder();
        order.setTotalAmount(NumberUtil.add(order.getDiscountAmount(), order.getDiscountedAmount()));
        order.setMerchantId(merchantId);
        if (order.getId() != null) {
            SalesOrder original = salesOrderRepository.getById(order.getId());

            BeanUtil.copyProperties(order, original, CopyOptions.create().ignoreNullValue());
            Set<Long> ids = new HashSet<>();
            for (SalesOrderDetail d : orderForm.getSalesOrderDetailList()) {
                if (d.getId() != null) {
                    ids.add(d.getId());
                }
                d.setSalesOrderId(order.getId());
                d.setMerchantId(merchantId);
                d.setOrganizationId(organizationId);
            }
            detailRepository.saveAll(orderForm.getSalesOrderDetailList());
            salesOrderRepository.save(original);
        } else {
            String code = "XSD" + merchantCode + codeSeedService.dayIncrease(order.getMerchantId(), "XSD");
            order.setCode(code);
            order.setOrderStatus(OrderStatus.已保存);
            order.setUserId(adminId);
            order.setMerchantId(merchantId);
            order.setOrganizationId(organizationId);
            salesOrderRepository.save(order);
            for (SalesOrderDetail d : orderForm.getSalesOrderDetailList()) {
                d.setSalesOrderId(order.getId());
                d.setMerchantId(merchantId);
                d.setOrganizationId(organizationId);
            }
            detailRepository.saveAll(orderForm.getSalesOrderDetailList());
        }
    }

    public Dict load(Long merchantId, Long orderId, Long organizationId) {
        Tuple fetchFirst = jqf.selectFrom(qSalesOrder).select(qSalesOrder, qCustomers.name).leftJoin(qCustomers).on(qCustomers.id.eq(qSalesOrder.customersId)).where(qSalesOrder.merchantId.eq(merchantId).and(qSalesOrder.id.eq(orderId)).and(qSalesOrder.organizationId.eq(organizationId))).fetchFirst();

        SalesOrderDto order = BeanUtil.toBean(fetchFirst.get(qSalesOrder), SalesOrderDto.class);
        order.setCustomersName(fetchFirst.get(qCustomers.name));
        ArrayList<Dict> collect = jqf.selectFrom(qSalesOrderDetail)
                .select(qSalesOrderDetail, qProducts.code, qProducts.name,
                        qProducts.imgPath, qProducts.specification)
                .leftJoin(qProducts).on(qProducts.id.eq(qSalesOrderDetail.productsId).and(qProducts.merchantId.eq(merchantId)).and(qProducts.organizationId.eq(organizationId)))
                .where(qSalesOrderDetail.salesOrderId.eq(orderId).and(qSalesOrderDetail.merchantId.eq(merchantId)).and(qSalesOrderDetail.organizationId.eq(organizationId)))
                .orderBy(qSalesOrderDetail.id.asc())
                .fetch().stream().collect(ArrayList::new, (list, tuple) -> {
                    SalesOrderDetail od = tuple.get(qSalesOrderDetail);
                    Dict dict = Dict.create()
                            .set("id", od.getId())
                            .set("productsId", od.getProductsId())
                            .set("discountedAmount", od.getDiscountedAmount())
                            .set("orderPrice", od.getOrderPrice())
                            .set("orderQuantity", od.getOrderQuantity())
                            .set("sysQuantity", od.getSysQuantity())
                            .set("unitId", od.getUnitId())
                            .set("unitName", od.getUnitName())
                            .set("orderUnitId", od.getOrderUnitId())
                            .set("orderUnitName", od.getOrderUnitName())
                            .set("discount", od.getDiscount())
                            .set("remark", od.getRemark())
                            .set("discountAmount", od.getDiscountAmount())
                            .set("productsCode", tuple.get(qProducts.code))
                            .set("productsName", tuple.get(qProducts.name))
                            .set("spec", tuple.get(qProducts.specification))
                            .set("imgPath", tuple.get(qProducts.imgPath));
                    list.add(dict);
                }, List::addAll);
        return Dict.create().set("order", order).set("productsData", collect);
    }

    @Transactional
    public void updateState(Order order, AccountDto accountDto) {
        SalesOrder first = jqf.selectFrom(qSalesOrder).where(qSalesOrder.id.eq(order.getId()).and(qSalesOrder.merchantId.eq(accountDto.getMerchantId())).and(qSalesOrder.organizationId.eq(accountDto.getOrganizationId()))).fetchFirst();
        Assert.isFalse(first == null, "非法操作...");
        Assert.isTrue(first.getBillDate().isAfter(accountDto.getCheckDate()), "小于等于结账时间:" + accountDto.getCheckDate() + "不能修改数据");
        first.setOrderStatus(OrderStatus.已审核);
        first.setCheckId(accountDto.getAdminId());
        first.setCheckOutTime(LocalDateTime.now());
        salesOrderRepository.save(first);
    }


    @Data
    public static class RankQuery {
        private Long merchantId;
        private Long organizationId;
        private LocalDate start;
        private LocalDate end;
        private String filter;
    }

    public static class Query {
        public final BooleanBuilder builder = new BooleanBuilder();
        public String sortCol;

        public String sort = Constants.SORT_DESC;

        public void setSortCol(String sortCol) {
            this.sortCol = sortCol;
        }

        public void setSort(String sort) {
            this.sort = sort;
        }

        public OrderSpecifier sortSpecifier() {
            if ("createDate".equals(this.sortCol)) {
                if (Constants.SORT_DESC.equals(this.sort)) {
                    return qSalesOrder.createDate.desc();
                } else {
                    return qSalesOrder.createDate.asc();
                }
            } else if ("billDate".equals(this.sortCol)) {
                if (Constants.SORT_DESC.equals(this.sort)) {
                    return qSalesOrder.billDate.desc();
                } else {
                    return qSalesOrder.billDate.asc();
                }
            }
            return qSalesOrder.billDate.desc();
        }

        public void setState(OrderStatus state) {
            if (state != null) {
                builder.and(qSalesOrder.orderStatus.eq(state));
            }
        }

        public void setMerchantId(Long merchantId) {
            if (merchantId != null) {
                builder.and(qSalesOrder.merchantId.eq(merchantId));
            }
        }

        public void setOrganizationId(Long organizationId) {
            if (organizationId != null) {
                builder.and(qSalesOrder.organizationId.eq(organizationId));
            }
        }

        public void setFilter(String filter) {
            if (filter != null) {
                builder.and(qSalesOrder.code.contains(filter).or(qCustomers.name.contains(filter)));
            }
        }

        public void setStart(LocalDate start) {
            if (start != null) {
                builder.and(qSalesOrder.billDate.goe(start));
            }
        }

        public void setEnd(LocalDate end) {
            if (end != null) {
                builder.and(qSalesOrder.billDate.loe(end));
            }
        }
    }
}
